//    entity language compiler Copyright (C) 2011 Vincent Belliard

//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation version 3 of the License.

//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.

//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <typeinfo>
#include "types.h"
#include "entity.h"
#include "method.h"
#include "type.h"
#include "expression.h"
#include "object_class.h"

// }{--------------------------------------------------------------

void T_prototype_argument::dump(FILE *file) const
{
fprintf(file, "%s : ", name.string()) ;
if (type != NULL) type->dump(file) ;
if (default_expression != NULL)
	{
	fprintf(file, " = ") ;
	default_expression->dump(file) ;
	}
}

void T_prototype_argument::parse(T_entity_compiler *compiler)
{
//fprintf(stderr, "item <%u/%u>\n", compiler->current_item, entity_compiler_item_colon) ;
if (strcmp(name.string(), "self") == 0)
	{
	if (compiler->current_item == entity_compiler_item_colon)
		{
		compiler->next_item() ;
		while (compiler->current_item == entity_compiler_item_identifier) compiler->next_item() ;
		}
	}
if ((compiler->current_item == entity_compiler_item_colon) || (compiler->current_item == entity_compiler_item_equal))
	{
	compiler->next_item() ;
	type_expression = compiler->parse_logical_or_expression() ;
//fprintf(stderr, "expression=%p\n", type_expression) ;
	if (type_expression == NULL)
		{
		compiler->add_error(compiler->location, "type expected.") ;
		compiler->goto_right_parenthesis() ;
		return ;
		}
	if (compiler->current_item == entity_compiler_item_equal)
		{
		compiler->next_item() ;
		default_expression = compiler->parse_logical_or_expression() ;
		}
	}
}

void T_prototype_argument::verify_semantic(T_entity_compiler *compiler, T_entity_program *program, T_scope *scope, T_type_method *method_type, T_object_class *object_class)
{
if (strcmp(name.string(), "self") == 0)
	{
	if (type_expression != NULL) compiler->add_error(location, "self can't be defined.") ;
	else if (object_class == NULL) compiler->add_error(location, "self with class context.") ;
	else type = new T_type_identifier(location, object_class->complement_base) ;
	needs_entity = TRUE ;
	is_reference = TRUE ;
	}
else if (type_expression != NULL)
	{
	if (type_expression->verify_semantic(compiler, program, scope, method_type))
		{
		type = type_expression->get_type_expression() ;
		if ((type == NULL) && (type_expression->type == NULL)) compiler->add_error(location, "bad value for argument") ;
		if ((type != NULL) && type->is_reference()) is_reference = TRUE ;
		}
	}
else
	{
	T_object_class *object_class_loc = program->search_class(name) ;
	if (object_class_loc == NULL) compiler->add_error(location, "class %s not defined.", name.string()) ;
	else type = new T_type_reference(location, FALSE, new T_type_identifier(location, object_class_loc)) ;
	needs_entity = TRUE ;
	is_reference = TRUE ;
	}
}

int T_prototype_argument::can_overload(const T_prototype_argument *ref)
{
//fprintf(stderr, "compare arg %s and %s type=%p expr=%s\n", name.string(), ref->name.string(), type, (type_expression == NULL) ? "" : type_expression->get_name()) ;
if (strcmp(name.string(), ref->name.string()) != 0) return FALSE ;
if (strcmp(name.string(), "self") == 0) return TRUE ;
if (type != NULL)
	{
//	fprintf(stderr, "type=%d ref=%d\n", type->get_class(), ref->type->get_class()) ;
	return type->can_overload(ref->type) ;
	}
return type_expression->type->can_overload(ref->type) ;
}

// }{--------------------------------------------------------------

void T_prototype::dump(FILE *file) const
{
fprintf(file, "(") ;
const T_prototype_argument *argument = arguments.first ;
if (argument != NULL)
	{
	for(;;)
		{
		argument->dump(file) ;
		argument = argument->next ;
		if (argument == NULL) break ;
		fprintf(file, ", ") ;
		}
	}
fprintf(file, ")") ;
}

int T_prototype::parse(T_entity_compiler *compiler)
{
if (compiler->current_item != entity_compiler_item_left_parenthesis)
	{
	compiler->add_error(compiler->location, "prototype expected.") ;
	return FALSE ;
	}
compiler->next_item() ;
for(;;)
	{
	if (compiler->current_item == entity_compiler_item_right_parenthesis)
		{
		compiler->next_item() ;
		return TRUE ;
		}
	if (!compiler->is_identifier())
		{
		compiler->add_error(compiler->location, "argument expected.") ;
		return FALSE ;
		}
	T_entity_location where(compiler->file_name, compiler->location) ;
	T_prototype_argument *argument = new T_prototype_argument(where, compiler->string) ;
	arguments.insert(argument) ;
	compiler->next_item() ;
//fprintf(stderr, "parse arg <%s> current(%d)<%.20s>\n", argument->name.string(), compiler->current_item, compiler->location.get_content()->data + compiler->location.offset) ;
	argument->parse(compiler) ;
//fprintf(stderr, "current <%.20s>\n", compiler->location.get_content()->data + compiler->location.offset) ;
	if (compiler->current_item != entity_compiler_item_comma)
		{
		if (compiler->current_item != entity_compiler_item_right_parenthesis)
			{
			compiler->add_error(compiler->location, "comma or right parenthesis expected.") ;
			return FALSE ;
			}
		compiler->next_item() ;
		return TRUE ;
		}
	compiler->next_item() ;
	}
}

void T_prototype::verify_semantic(T_entity_compiler *compiler, T_entity_program *program, T_scope *scope, T_type_method *method_type, T_object_class *object_class)
{
T_prototype_argument *argument = arguments.first ;
while (argument != NULL)
	{
	argument->verify_semantic(compiler, program, scope, method_type, object_class) ;
	if (argument->type == NULL) specialization = TRUE ;
	argument = argument->next ;
	}
}

int T_prototype::can_overload(const T_prototype *ref) const
{
//fprintf(stderr, "args=%d/%d\n", arguments.count, ref->arguments.count) ;
if (arguments.count != ref->arguments.count) return FALSE ;
T_prototype_argument *arg_cmp = arguments.first ;
T_prototype_argument *arg_ref = ref->arguments.first ;
while (arg_cmp != NULL)
	{
	if (!arg_cmp->can_overload(arg_ref)) return FALSE ;
	arg_cmp = arg_cmp->next ;
	arg_ref = arg_ref->next ;
	}
return TRUE ;
}

int T_prototype::is_good_for_call(T_entity_program *program, T_expression_constructor_arguments *call) const
{
T_prototype_argument *argument = arguments.first ;
T_expression *expression = call->expressions.first ;
while (argument != NULL)
	{
//fprintf(stderr, "try arg %s expr_type=%d\n", argument->name.string(), (expression == NULL) ? 0 : expression->type->get_class()) ;
	if (strcmp(argument->name.string(), "self") == 0)
		{
		}
	else if ((expression != NULL) && argument->type->is_good_for_call(program, expression->type))
		{
		expression = expression->next ;
		}
	else if ((expression == NULL) && (argument->next == NULL) && (argument->type->get_class() == class_type_vector))
		{
		}
	else
		{
		return FALSE ;
		}
	argument = argument->next ;
	}
//fprintf(stderr, "expression=%p\n", expression) ;
return expression == NULL ;
}

int T_prototype::is_good_for_constructor(T_entity_program *program, T_expression_constructor_arguments *constructor) const
{
T_prototype_argument *argument = arguments.first ;
T_expression *expression = (constructor == NULL) ? NULL : constructor->expressions.first ;
while (argument != NULL)
	{
//fprintf(stderr, "try arg %s expr_type=%d\n", argument->name.string(), (expression == NULL) ? 0 : expression->type->get_class()) ;
	if (strcmp(argument->name.string(), "self") == 0)
		{
		}
	else if ((expression != NULL) && argument->type->is_good_for_call(program, expression->type))
		{
		expression = expression->next ;
		}
	else if ((expression == NULL) && (argument->next == NULL) && (argument->type != NULL) && (argument->type->get_class() == class_type_vector))
		{
		}
	else
		{
		return FALSE ;
		}
	argument = argument->next ;
	}
return expression == NULL ;
}

void T_prototype::generate_prototype(FILE *file, int first) const
{
if (first) fprintf(file, "(") ;
T_prototype_argument *argument = arguments.first ;
while (argument != NULL)
	{
	if (first) first = FALSE ;
	else fprintf(file, ", ") ;
	if (argument->type_expression == NULL)
		{
		if (argument->class_call && argument->needs_entity)
			{
			argument->type->generate_type_name(file, type_generate_introspect_reference) ;
			fprintf(file, "varg_%s", argument->name.string()) ;
			argument->type->generate_type_name(file, type_generate_class_reference) ;
			fprintf(file, "carg_%s", argument->name.string()) ;
			}
		else
			{
			argument->type->generate_type_name(file, type_generate_class_reference) ;
			fprintf(file, "arg_%s", argument->name.string()) ;
			}
		}
	else
		{
		if (argument->class_call && argument->needs_entity)
			{
			argument->type->generate_type_name(file, type_generate_introspect_reference) ;
			fprintf(file, "varg_%s", argument->name.string()) ;
			argument->type->generate_type_name(file, type_generate_value) ;
			fprintf(file, " carg_%s", argument->name.string()) ;
			}
		else
			{
			argument->type->generate_type_name(file, type_generate_value) ;
			fprintf(file, " arg_%s", argument->name.string()) ;
			}
		}
	argument = argument->next ;
	}
fprintf(file, ")") ;
}

void T_prototype::generate_specialization(T_entity_program *program, FILE *file, int false_label)
{
T_prototype_argument *argument = arguments.first ;
while (argument != NULL)
	{
	if (argument->type == NULL)
		{
		argument->type_expression->prepare(program, file) ;
		fprintf(file, "if (arg_%s != ", argument->name.string()) ;
		argument->type_expression->generate(program, file, type_generate_value) ;
		fprintf(file, ") goto label_%d ;\n", false_label) ;
		}
	argument = argument->next ;
	}
}

// }{--------------------------------------------------------------
